Replication of Fig 2 of Christenson et al 2010
dta_Mx %>%
filter(sex != "total") %>%
filter(age %in% c(80, 90)) %>%
filter(
code %in% c("GBRTENW", "FRATNP", "DEUTE", "DEUTW", "JPN", "SWE", "USA")
) %>%
filter(between(year, 1950, 2003)) %>%
ggplot(
aes(x = year, y = Mx, colour = code, group = code)
) +
geom_line() +
facet_grid(age ~ sex)

And how has this developed since?
dta_Mx %>%
filter(sex != "total") %>%
filter(age %in% c(80, 90)) %>%
filter(
code %in% c("GBRTENW", "FRATNP", "DEUTE", "DEUTW", "JPN", "SWE", "USA")
) %>%
filter(between(year, 1950, 2017)) %>%
ggplot(
aes(x = year, y = Mx, colour = code, group = code)
) +
geom_line() +
facet_grid(age ~ sex)

It’s interesting that life expectancies have at age 80 have converged, for males especially. And that they’ve levelled off in Japan over the 2000s, while continued to improve in the USA.
In England/Wales they Mx at age 90 seem to have increased for females especially in recent years.
Let’s look at this for the average of the 21 countries
Now let’s do this for the average of the 21 high income countries
dta_Mx %>%
filter(sex != "total") %>%
filter(age %in% c(80, 90)) %>%
filter(code %in% high_income_countries) %>%
filter(between(year, 1950, 2016)) %>%
group_by(year, age, sex) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
ggplot(aes(x = year, y = mean_Mx, colour = sex)) +
facet_wrap(~age) +
geom_point()

Let’s now use log rather than linear
dta_Mx %>%
filter(sex != "total") %>%
filter(age %in% c(80, 90)) %>%
filter(code %in% high_income_countries) %>%
filter(between(year, 1950, 2016)) %>%
group_by(year, age, sex) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
ggplot(aes(x = year, y = mean_Mx, colour = sex)) +
facet_wrap(~age) +
geom_point() +
scale_y_log10()

So, the trends on % reductions have been more continuous for age 90 than age 80, and more continuous for females than males.
Let’s now do this for a couple of additional age groups
dta_Mx %>%
filter(sex != "total") %>%
filter(age %in% c(0, 30, 80, 90)) %>%
filter(code %in% high_income_countries) %>%
filter(between(year, 1950, 2016)) %>%
group_by(year, age, sex) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
ggplot(aes(x = year, y = mean_Mx, colour = sex)) +
facet_wrap(~age) +
geom_point() +
scale_y_log10()

Now let’s estimate the correlation between these trends
dta_trnd <- dta_Mx %>%
filter(sex == "total") %>%
filter(between(year, 1955, 2016)) %>%
filter(age %in% c(0, 30, 80, 90)) %>%
group_by(year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx, 10))
dta_trnd %>%
select(-mean_Mx) %>%
mutate(age = paste0("age_", age)) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
age_0 age_30 age_80 age_90
age_0 1.0000000 0.9201582 0.9765313 0.9827364
age_30 0.9201582 1.0000000 0.9502384 0.9410485
age_80 0.9765313 0.9502384 1.0000000 0.9855633
age_90 0.9827364 0.9410485 0.9855633 1.0000000
So, as expected, % improvements in infancy are more strongly correlated with those at age 80 and 90 than at age 30, and % changes at age 90 are less strongly correlated than with those at other ages.
Let’s do this as a heatmap for all ages
dta_trnd <- dta_Mx %>%
filter(sex == "total") %>%
filter(between(year, 1955, 2016)) %>%
filter(age <= 109) %>%
group_by(year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx, 10))
tmp <- dta_trnd %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
cor_df <- tmp %>%
as_tibble() %>%
mutate(from_age = rownames(tmp)) %>%
gather(key="to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
cor_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal()

Definitely a ‘wow’ figure!
Interesting that the trends that apply at older ages don’t apply at the oldest ages (from around age 96 onwards)
Let’s see how different it looks by gender.
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(between(year, 1955, 2016)) %>%
filter(age <= 109) %>%
group_by(sex, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx, 10)) %>%
group_by(sex) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_wrap(~sex)

Again, beautiful, staggering, and awesome. This is like seeing the genome of population health improvement being mapped. The gender differences in the differences in correlation are very apparent.
Let’s extend this to a few few select countries:
- France
- Sweden?
- UK
- USA
- Spain (Identified as most compatible with Lee-Carter modelling approach)
- Japan
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("FRATNP", "SWE", "GBR_NP", "USA", "ESP", "JPN")) %>%
filter(between(year, 1955, 2016)) %>%
filter(age <= 109) %>%
group_by(code, sex, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
group_by(sex, code) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, code, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~ code)

Let’s do this just for the UK nations
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("GBRTENW", "GBR_SCO", "GBR_NIR")) %>%
filter(between(year, 1955, 2016)) %>%
filter(age <= 109) %>%
group_by(code, sex, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
group_by(sex, code) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, code, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c(limits = c(-1,1)) +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~code) +
labs(x = "Age", y = "Age", title = "Correlations between rates of change in log mortality risk age different ages",
subtitle = "Trends from 1955-2016. England & Wales, Scotland, Northern Ireland",
caption = "Source: Human Mortality Database. Available from https://github.com/JonMinton/Life_expectancy_limits/")
ggsave("figures/trend_correlation_heatmap.png", height = 20, width = 25, units = "cm", dpi = 300)

Although plotly doesn’t convert faceting too easily, can I use ggplotly for hovering over values for Scotland males alone?
ggplotly now works with faceted plots! (Though the number of observations looks like it causes things to struggle!)
For Scotland and England & Wales (independently), how have the correlations changed over time?
England/Wales first:
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("GBRTENW")) %>%
filter(between(year, 1950, 2010)) %>%
filter(age <= 109) %>%
mutate(
decade = cut(year, breaks = seq(1950, 2010, by = 10), labels = c("1950s", "1960s", "1970s", "1980s", "1990s", "2000s"), include.lowest = TRUE)
) %>%
group_by(sex, decade, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx, 10)) %>% # Correction for Sweden
group_by(sex, decade) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, decade, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~ decade)

Now for Scotland.
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("GBR_SCO")) %>%
filter(between(year, 1950, 2010)) %>%
filter(age <= 109) %>%
mutate(
decade = cut(year, breaks = seq(1950, 2010, by = 10), labels = c("1950s", "1960s", "1970s", "1980s", "1990s", "2000s"), include.lowest = TRUE)
) %>%
group_by(sex, decade, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
group_by(sex, decade) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, decade, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~ decade)

Northern Ireland
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("GBR_NIR")) %>%
filter(between(year, 1950, 2010)) %>%
filter(age <= 109) %>%
mutate(
decade = cut(year, breaks = seq(1950, 2010, by = 10), labels = c("1950s", "1960s", "1970s", "1980s", "1990s", "2000s"), include.lowest = TRUE)
) %>%
group_by(sex, decade, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
group_by(sex, decade) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, decade, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~ decade)

dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("GBRTENW")) %>%
filter(between(year, 1955, 2015)) %>%
filter(age <= 109) %>%
mutate(
decade = cut(year, breaks = seq(1955, 2015, by = 10), include.lowest = TRUE)
) %>%
group_by(sex, decade, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx, 10)) %>% # Correction for Sweden
group_by(sex, decade) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, decade, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~ decade)

Now for Scotland.
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("GBR_SCO")) %>%
filter(between(year, 1955, 2015)) %>%
filter(age <= 109) %>%
mutate(
decade = cut(year, breaks = seq(1955, 2015, by = 10), include.lowest = TRUE)
) %>%
group_by(sex, decade, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
group_by(sex, decade) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, decade, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~ decade)

LS0tDQp0aXRsZTogIkFnZSBTcGVjaWZpYyBNb3J0YWxpdHkgUmF0ZSBUcmVuZHMgIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBJbnRybw0KDQpUaGlzIGRvYyB3aWxsIGRlc2NyaWJlIHNvbWUgb2YgdGhlIGF2ZXJhZ2UgdHJlbmRzIGF0IHNwZWNpZmljIGFnZXMgZnJvbSAyMSBoaWdoIGluY29tZSBjb3VudHJpZXMuDQoNCkl0IGlzIGluc3BpcmVkIGJ5IFtDaHJpc3RlbnNlbiAyMDEwXShodHRwczovL3d3dy50aGVsYW5jZXQuY29tL2pvdXJuYWxzL2xhbmNldC9hcnRpY2xlL1BJSVMwMTQwLTY3MzYoMDkpNjE0NjAtNC9mdWxsdGV4dCksIHdoaWNoIHNob3dlZA0KDQoqIFByb2JhYmlsaXR5IG9mIGR5aW5nIGluIG5leHQgMTIgbW9udGhzDQoqIEF0IGFnZSA4MCBhbmQgYWdlIDkwDQoqIE1hbGVzIGFuZCBmZW1hbGVzIA0KKiBTZWxlY3RlZCBjb3VudHJpZXMgDQogICAgKiBFbmdsYW5kICYgV2FsZXMNCiAgICAqIEZyYW5jZQ0KICAgICogRWFzdCBHZXJtYW55DQogICAgKiBXZXN0IEdlcm1hbnkNCiAgICAqIEphcGFuDQogICAgKiBTd2VkZW4NCiAgICAqIFVTQQ0KICANCiAgDQpIb3dldmVyLCBhcyBwZXIgdGhlIGZpZ3VyZXMgaW4gW1doaXRlIDIwMDJdKGh0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pL3BkZi8xMC4xMTExL2ouMTcyOC00NDU3LjIwMDIuMDAwNTkueCkpLCB0aGUgYXZlcmFnZSBvZiAyMSBoaWdoIGluY29tZSBjb3VudHJpZXMgd2lsbCBiZSB1c2VkIGluc3RlYWQsIGFzIHdlbGwgYXMgdGhlIGJlc3QtcGVyZm9ybWluZyBjb3VudHJ5IGF0IHRoZXNlIGRpZmZlcmVudCBhZ2UgZ3JvdXBzLiANCg0KQWRkaXRpb25hbGx5LCB0aGUgMTIgbW9udGggbW9ydGFsaXR5IHByb2JhYmlsaXRpZXMgYXQgdGhlIGZvbGxvd2luZyBhZ2VzIHdpbGwgYWxzbyBiZSBjYWxjdWxhdGVkOg0KDQoqIDAtMSB5ZWFycw0KKiA0MCB5ZWFycw0KDQpUaGUgYWltIHdpbGwgYmUgdG8gZGV0ZXJtaW5lIGhvdyBzdHJvbmdseSB0aGUgKGxvZyBvZiB0aGVzZSkgdHJlbmRzIGFyZSBjb3JyZWxhdGVkLiANCg0KIyBQcmUgcmVxcw0KDQoNCmBgYHtyfQ0KcGFjbWFuOjpwX2xvYWQoDQogIHRpZHl2ZXJzZSwgSE1ESEZEcGx1cywNCiAgZ2dyZXBlbCwgcGxvdGx5DQopDQoNCmR0YV9NeCA8LSByZWFkX3JkcygidGlkeV9kYXRhL014X2RhdGEucmRzIikNCg0KYGBgDQoNCiBEZWZpbmUgY291bnRyaWVzIA0KYGBge3J9DQpzb3VyY2UoInNjcmlwdHMvY291bnRyeV9kZWZpbml0aW9ucy5SIikNCg0KDQpgYGANCg0KIyBSZXBsaWNhdGlvbiBvZiBGaWcgMiBvZiBDaHJpc3RlbnNvbiBldCBhbCAyMDEwDQoNCmBgYHtyfQ0KDQpkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ICE9ICJ0b3RhbCIpICU+JSANCiAgZmlsdGVyKGFnZSAlaW4lIGMoODAsIDkwKSkgJT4lIA0KICBmaWx0ZXIoDQogICAgY29kZSAlaW4lIGMoIkdCUlRFTlciLCAiRlJBVE5QIiwgIkRFVVRFIiwgIkRFVVRXIiwgIkpQTiIsICJTV0UiLCAiVVNBIikNCiAgKSAlPiUgICAgICANCiAgZmlsdGVyKGJldHdlZW4oeWVhciwgMTk1MCwgMjAwMykpICU+JSANCiAgZ2dwbG90KA0KICAgIGFlcyh4ID0geWVhciwgeSA9IE14LCBjb2xvdXIgPSBjb2RlLCBncm91cCA9IGNvZGUpDQogICkgKyANCiAgZ2VvbV9saW5lKCkgKw0KICBmYWNldF9ncmlkKGFnZSB+IHNleCkNCg0KYGBgDQoNCkFuZCBob3cgaGFzIHRoaXMgZGV2ZWxvcGVkIHNpbmNlPw0KDQpgYGB7cn0NCg0KZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUgDQogIGZpbHRlcihhZ2UgJWluJSBjKDgwLCA5MCkpICU+JSANCiAgZmlsdGVyKA0KICAgIGNvZGUgJWluJSBjKCJHQlJURU5XIiwgIkZSQVROUCIsICJERVVURSIsICJERVVUVyIsICJKUE4iLCAiU1dFIiwgIlVTQSIpDQogICkgJT4lICAgICAgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTAsIDIwMTcpKSAlPiUgDQogIGdncGxvdCgNCiAgICBhZXMoeCA9IHllYXIsIHkgPSBNeCwgY29sb3VyID0gY29kZSwgZ3JvdXAgPSBjb2RlKQ0KICApICsgDQogIGdlb21fbGluZSgpICsNCiAgZmFjZXRfZ3JpZChhZ2UgfiBzZXgpDQoNCmBgYA0KDQpJdCdzIGludGVyZXN0aW5nIHRoYXQgbGlmZSBleHBlY3RhbmNpZXMgaGF2ZSBhdCBhZ2UgODAgaGF2ZSBjb252ZXJnZWQsIGZvciBtYWxlcyBlc3BlY2lhbGx5LiANCkFuZCB0aGF0IHRoZXkndmUgbGV2ZWxsZWQgb2ZmIGluIEphcGFuIG92ZXIgdGhlIDIwMDBzLCB3aGlsZSBjb250aW51ZWQgdG8gaW1wcm92ZSBpbiB0aGUgVVNBLiANCg0KSW4gRW5nbGFuZC9XYWxlcyB0aGV5IE14IGF0IGFnZSA5MCBzZWVtIHRvIGhhdmUgaW5jcmVhc2VkIGZvciBmZW1hbGVzIGVzcGVjaWFsbHkgaW4gcmVjZW50IHllYXJzLiANCg0KTGV0J3MgbG9vayBhdCB0aGlzIGZvciB0aGUgYXZlcmFnZSBvZiB0aGUgMjEgY291bnRyaWVzIA0KDQpOb3cgbGV0J3MgZG8gdGhpcyBmb3IgdGhlIGF2ZXJhZ2Ugb2YgdGhlIDIxIGhpZ2ggaW5jb21lIGNvdW50cmllcyANCg0KYGBge3J9DQoNCmR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lIA0KICBmaWx0ZXIoYWdlICVpbiUgYyg4MCwgOTApKSAlPiUgDQogIGZpbHRlcihjb2RlICVpbiUgaGlnaF9pbmNvbWVfY291bnRyaWVzKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTAsIDIwMTYpKSAlPiUgDQogIGdyb3VwX2J5KHllYXIsIGFnZSwgc2V4KSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gbWVhbl9NeCwgY29sb3VyID0gc2V4KSkgKyANCiAgZmFjZXRfd3JhcCh+YWdlKSArDQogIGdlb21fcG9pbnQoKQ0KDQoNCmBgYA0KDQpMZXQncyBub3cgdXNlIGxvZyByYXRoZXIgdGhhbiBsaW5lYXIgDQoNCmBgYHtyfQ0KZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUgDQogIGZpbHRlcihhZ2UgJWluJSBjKDgwLCA5MCkpICU+JSANCiAgZmlsdGVyKGNvZGUgJWluJSBoaWdoX2luY29tZV9jb3VudHJpZXMpICU+JSANCiAgZmlsdGVyKGJldHdlZW4oeWVhciwgMTk1MCwgMjAxNikpICU+JSANCiAgZ3JvdXBfYnkoeWVhciwgYWdlLCBzZXgpICU+JSANCiAgc3VtbWFyaXNlKG1lYW5fTXggPSBtZWFuKE14LCBuYS5ybSA9IFQpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBtZWFuX014LCBjb2xvdXIgPSBzZXgpKSArIA0KICBmYWNldF93cmFwKH5hZ2UpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfeV9sb2cxMCgpDQoNCg0KYGBgDQoNClNvLCB0aGUgdHJlbmRzIG9uICUgcmVkdWN0aW9ucyBoYXZlIGJlZW4gbW9yZSBjb250aW51b3VzIGZvciBhZ2UgOTAgdGhhbiBhZ2UgODAsIGFuZCBtb3JlIGNvbnRpbnVvdXMgZm9yIGZlbWFsZXMgdGhhbiBtYWxlcy4gDQoNCkxldCdzIG5vdyBkbyB0aGlzIGZvciBhIGNvdXBsZSBvZiBhZGRpdGlvbmFsIGFnZSBncm91cHMgDQoNCmBgYHtyfQ0KZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUgDQogIGZpbHRlcihhZ2UgJWluJSBjKDAsIDMwLCA4MCwgOTApKSAlPiUgDQogIGZpbHRlcihjb2RlICVpbiUgaGlnaF9pbmNvbWVfY291bnRyaWVzKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTAsIDIwMTYpKSAlPiUgDQogIGdyb3VwX2J5KHllYXIsIGFnZSwgc2V4KSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gbWVhbl9NeCwgY29sb3VyID0gc2V4KSkgKyANCiAgZmFjZXRfd3JhcCh+YWdlKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX3lfbG9nMTAoKQ0KDQoNCmBgYA0KDQpOb3cgbGV0J3MgZXN0aW1hdGUgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlc2UgdHJlbmRzDQoNCmBgYHtyfQ0KZHRhX3RybmQgPC0gZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCA9PSAidG90YWwiKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTUsIDIwMTYpKSAgJT4lIA0KICBmaWx0ZXIoYWdlICVpbiUgYygwLCAzMCwgODAsIDkwKSkgJT4lIA0KICBncm91cF9ieSh5ZWFyLCBhZ2UpICU+JSANCiAgc3VtbWFyaXNlKG1lYW5fTXggPSBtZWFuKE14LCBuYS5ybSA9IFQpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShsb2dfbWVhbl9NeCA9IGxvZyhtZWFuX014LCAxMCkpIA0KDQpkdGFfdHJuZCAlPiUgDQogIHNlbGVjdCgtbWVhbl9NeCkgJT4lIA0KICBtdXRhdGUoYWdlID0gcGFzdGUwKCJhZ2VfIiwgYWdlKSkgJT4lIA0KICBzcHJlYWQoYWdlLCBsb2dfbWVhbl9NeCkgJT4lDQogIHNlbGVjdCgteWVhcikgJT4lIA0KICBjb3IoKQ0KDQpgYGANCg0KU28sIGFzIGV4cGVjdGVkLCAlIGltcHJvdmVtZW50cyBpbiBpbmZhbmN5IGFyZSBtb3JlIHN0cm9uZ2x5IGNvcnJlbGF0ZWQgd2l0aCB0aG9zZSBhdCBhZ2UgODAgYW5kIDkwIHRoYW4gYXQgYWdlIDMwLCBhbmQgJSBjaGFuZ2VzIGF0IGFnZSA5MCBhcmUgbGVzcyBzdHJvbmdseSBjb3JyZWxhdGVkIHRoYW4gd2l0aCB0aG9zZSBhdCBvdGhlciBhZ2VzLiANCg0KTGV0J3MgZG8gdGhpcyBhcyBhIGhlYXRtYXAgZm9yIGFsbCBhZ2VzIA0KDQpgYGB7cn0NCmR0YV90cm5kIDwtIGR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggPT0gInRvdGFsIikgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTU1LCAyMDE2KSkgICU+JSANCiAgZmlsdGVyKGFnZSA8PSAxMDkpICU+JSANCiAgZ3JvdXBfYnkoeWVhciwgYWdlKSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUobG9nX21lYW5fTXggPSBsb2cobWVhbl9NeCwgMTApKSANCg0KdG1wIDwtIGR0YV90cm5kICU+JSANCiAgc2VsZWN0KC1tZWFuX014KSAlPiUgDQogIHNwcmVhZChhZ2UsIGxvZ19tZWFuX014KSAlPiUNCiAgc2VsZWN0KC15ZWFyKSAlPiUgDQogIGNvcigpIA0KDQpjb3JfZGYgPC0gdG1wICU+JSANCiAgYXNfdGliYmxlKCkgJT4lIA0KICBtdXRhdGUoZnJvbV9hZ2UgPSByb3duYW1lcyh0bXApKSAlPiUgDQogIGdhdGhlcihrZXk9InRvX2FnZSIsIHZhbHVlID0gInZhbHVlIiwgLWZyb21fYWdlKSAlPiUgDQogIG11dGF0ZShmcm9tX2FnZSA9IGFzLm51bWVyaWMoZnJvbV9hZ2UpLCB0b19hZ2UgPSBhcy5udW1lcmljKHRvX2FnZSkpDQoNCmNvcl9kZiAlPiUgDQogIGZpbHRlcihmcm9tX2FnZSA8PSAxMDAsIHRvX2FnZSA8PSAxMDApICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZnJvbV9hZ2UsIHkgPSB0b19hZ2UsIGZpbGwgPSB2YWx1ZSkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgY29vcmRfZXF1YWwoKQ0KDQpgYGANCg0KDQpEZWZpbml0ZWx5IGEgJ3dvdycgZmlndXJlISANCg0KSW50ZXJlc3RpbmcgdGhhdCB0aGUgdHJlbmRzIHRoYXQgYXBwbHkgYXQgb2xkZXIgYWdlcyBkb24ndCBhcHBseSBhdCB0aGUgb2xkZXN0IGFnZXMgKGZyb20gYXJvdW5kIGFnZSA5NiBvbndhcmRzKQ0KDQpMZXQncyBzZWUgaG93IGRpZmZlcmVudCBpdCBsb29rcyBieSBnZW5kZXIuDQoNCmBgYHtyfQ0KZHRhX3RybmQgPC0gZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTUsIDIwMTYpKSAgJT4lIA0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBncm91cF9ieShzZXgsIHllYXIsIGFnZSkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGxvZ19tZWFuX014ID0gbG9nKG1lYW5fTXgsIDEwKSkgJT4lIA0KICBncm91cF9ieShzZXgpICU+JSANCiAgbmVzdCgpDQoNCg0KY29yc19kZiA8LSBkdGFfdHJuZCAlPiUgDQogIG11dGF0ZShjb3JzID0gbWFwKA0KICAgIGRhdGEsIA0KICAgIGZ1bmN0aW9uKFgpIHsNCiAgICAgIFggJT4lIA0KICAgICAgICBzZWxlY3QoLW1lYW5fTXgpICU+JSANCiAgICAgICAgc3ByZWFkKGFnZSwgbG9nX21lYW5fTXgpICU+JSANCiAgICAgICAgc2VsZWN0KC15ZWFyKSAlPiUgDQogICAgICAgIGNvcigpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgbXV0YXRlKGNvcl9kZiA9IG1hcCgNCiAgICBjb3JzLA0KICAgIGZ1bmN0aW9uKFgpew0KICAgICAgWCAlPiUgDQogICAgICAgIGFzX3RpYmJsZSgpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gcm93bmFtZXMoWCkpICU+JSANCiAgICAgICAgZ2F0aGVyKGtleSA9ICJ0b19hZ2UiLCB2YWx1ZSA9ICJ2YWx1ZSIsIC1mcm9tX2FnZSkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSBhcy5udW1lcmljKGZyb21fYWdlKSwgdG9fYWdlID0gYXMubnVtZXJpYyh0b19hZ2UpKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIHNlbGVjdChzZXgsIGNvcl9kZikgJT4lIA0KICB1bm5lc3QoKQ0KDQoNCmNvcnNfZGYgJT4lIA0KICBmaWx0ZXIoZnJvbV9hZ2UgPD0gMTAwLCB0b19hZ2UgPD0gMTAwKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZyb21fYWdlLCB5ID0gdG9fYWdlLCBmaWxsID0gdmFsdWUpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIGNvb3JkX2VxdWFsKCkgKyANCiAgZmFjZXRfd3JhcCh+c2V4KQ0KDQpgYGANCg0KQWdhaW4sIGJlYXV0aWZ1bCwgc3RhZ2dlcmluZywgYW5kIGF3ZXNvbWUuIFRoaXMgaXMgbGlrZSBzZWVpbmcgdGhlIGdlbm9tZSBvZiBwb3B1bGF0aW9uIGhlYWx0aCBpbXByb3ZlbWVudCBiZWluZyBtYXBwZWQuIFRoZSBnZW5kZXIgZGlmZmVyZW5jZXMgaW4gdGhlIGRpZmZlcmVuY2VzIGluIGNvcnJlbGF0aW9uIGFyZSB2ZXJ5IGFwcGFyZW50LiANCg0KTGV0J3MgZXh0ZW5kIHRoaXMgdG8gYSBmZXcgZmV3IHNlbGVjdCBjb3VudHJpZXM6IA0KDQoqIEZyYW5jZQ0KKiBTd2VkZW4/DQoqIFVLDQoqIFVTQSANCiogU3BhaW4gKElkZW50aWZpZWQgYXMgbW9zdCBjb21wYXRpYmxlIHdpdGggTGVlLUNhcnRlciBtb2RlbGxpbmcgYXBwcm9hY2gpDQoqIEphcGFuDQoNCg0KYGBge3J9DQpkdGFfdHJuZCA8LSBkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ICE9ICJ0b3RhbCIpICU+JQ0KICBmaWx0ZXIoY29kZSAlaW4lIGMoIkZSQVROUCIsICJTV0UiLCAiR0JSX05QIiwgIlVTQSIsICJFU1AiLCAiSlBOIikpICU+JSANCiAgZmlsdGVyKGJldHdlZW4oeWVhciwgMTk1NSwgMjAxNikpICAlPiUgDQogIGZpbHRlcihhZ2UgPD0gMTA5KSAlPiUgDQogIGdyb3VwX2J5KGNvZGUsIHNleCwgeWVhciwgYWdlKSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUobG9nX21lYW5fTXggPSBsb2cobWVhbl9NeCArIDAuMDAwMDEsIDEwKSkgJT4lICMgQ29ycmVjdGlvbiBmb3IgU3dlZGVuDQogIGdyb3VwX2J5KHNleCwgY29kZSkgJT4lIA0KICBuZXN0KCkNCg0KDQpjb3JzX2RmIDwtIGR0YV90cm5kICU+JSANCiAgbXV0YXRlKGNvcnMgPSBtYXAoDQogICAgZGF0YSwgDQogICAgZnVuY3Rpb24oWCkgew0KICAgICAgWCAlPiUgDQogICAgICAgIHNlbGVjdCgtbWVhbl9NeCkgJT4lIA0KICAgICAgICBzcHJlYWQoYWdlLCBsb2dfbWVhbl9NeCkgJT4lIA0KICAgICAgICBzZWxlY3QoLXllYXIpICU+JSANCiAgICAgICAgY29yKCkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBtdXRhdGUoY29yX2RmID0gbWFwKA0KICAgIGNvcnMsDQogICAgZnVuY3Rpb24oWCl7DQogICAgICBYICU+JSANCiAgICAgICAgYXNfdGliYmxlKCkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSByb3duYW1lcyhYKSkgJT4lIA0KICAgICAgICBnYXRoZXIoa2V5ID0gInRvX2FnZSIsIHZhbHVlID0gInZhbHVlIiwgLWZyb21fYWdlKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IGFzLm51bWVyaWMoZnJvbV9hZ2UpLCB0b19hZ2UgPSBhcy5udW1lcmljKHRvX2FnZSkpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgc2VsZWN0KHNleCwgY29kZSwgY29yX2RmKSAlPiUgDQogIHVubmVzdCgpDQoNCg0KY29yc19kZiAlPiUgDQogIGZpbHRlcihmcm9tX2FnZSA8PSAxMDAsIHRvX2FnZSA8PSAxMDApICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZnJvbV9hZ2UsIHkgPSB0b19hZ2UsIGZpbGwgPSB2YWx1ZSkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgY29vcmRfZXF1YWwoKSArIA0KICBmYWNldF9ncmlkKHNleCB+IGNvZGUpDQoNCmBgYA0KDQoNCkxldCdzIGRvIHRoaXMganVzdCBmb3IgdGhlIFVLIG5hdGlvbnMgDQoNCmBgYHtyfQ0KZHRhX3RybmQgPC0gZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUNCiAgZmlsdGVyKGNvZGUgJWluJSBjKCJHQlJURU5XIiwgIkdCUl9TQ08iLCAiR0JSX05JUiIpKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTUsIDIwMTYpKSAgJT4lIA0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBncm91cF9ieShjb2RlLCBzZXgsIHllYXIsIGFnZSkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGxvZ19tZWFuX014ID0gbG9nKG1lYW5fTXggKyAwLjAwMDAxLCAxMCkpICU+JSAjIENvcnJlY3Rpb24gZm9yIFN3ZWRlbg0KICBncm91cF9ieShzZXgsIGNvZGUpICU+JSANCiAgbmVzdCgpDQoNCg0KY29yc19kZiA8LSBkdGFfdHJuZCAlPiUgDQogIG11dGF0ZShjb3JzID0gbWFwKA0KICAgIGRhdGEsIA0KICAgIGZ1bmN0aW9uKFgpIHsNCiAgICAgIFggJT4lIA0KICAgICAgICBzZWxlY3QoLW1lYW5fTXgpICU+JSANCiAgICAgICAgc3ByZWFkKGFnZSwgbG9nX21lYW5fTXgpICU+JSANCiAgICAgICAgc2VsZWN0KC15ZWFyKSAlPiUgDQogICAgICAgIGNvcigpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgbXV0YXRlKGNvcl9kZiA9IG1hcCgNCiAgICBjb3JzLA0KICAgIGZ1bmN0aW9uKFgpew0KICAgICAgWCAlPiUgDQogICAgICAgIGFzX3RpYmJsZSgpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gcm93bmFtZXMoWCkpICU+JSANCiAgICAgICAgZ2F0aGVyKGtleSA9ICJ0b19hZ2UiLCB2YWx1ZSA9ICJ2YWx1ZSIsIC1mcm9tX2FnZSkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSBhcy5udW1lcmljKGZyb21fYWdlKSwgdG9fYWdlID0gYXMubnVtZXJpYyh0b19hZ2UpKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIHNlbGVjdChzZXgsIGNvZGUsIGNvcl9kZikgJT4lIA0KICB1bm5lc3QoKQ0KDQoNCmNvcnNfZGYgJT4lIA0KICBmaWx0ZXIoZnJvbV9hZ2UgPD0gMTAwLCB0b19hZ2UgPD0gMTAwKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZyb21fYWdlLCB5ID0gdG9fYWdlLCBmaWxsID0gdmFsdWUpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKGxpbWl0cyA9IGMoLTEsMSkpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBjb29yZF9lcXVhbCgpICsgDQogIGZhY2V0X2dyaWQoc2V4IH5jb2RlKSArIA0KICBsYWJzKHggPSAiQWdlIiwgeSA9ICJBZ2UiLCB0aXRsZSA9ICJDb3JyZWxhdGlvbnMgYmV0d2VlbiByYXRlcyBvZiBjaGFuZ2UgaW4gbG9nIG1vcnRhbGl0eSByaXNrIGFnZSBkaWZmZXJlbnQgYWdlcyIsDQogICAgICAgc3VidGl0bGUgPSAiVHJlbmRzIGZyb20gMTk1NS0yMDE2LiBFbmdsYW5kICYgV2FsZXMsIFNjb3RsYW5kLCBOb3J0aGVybiBJcmVsYW5kIiwNCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogSHVtYW4gTW9ydGFsaXR5IERhdGFiYXNlLiBBdmFpbGFibGUgZnJvbSBodHRwczovL2dpdGh1Yi5jb20vSm9uTWludG9uL0xpZmVfZXhwZWN0YW5jeV9saW1pdHMvIikNCg0KZ2dzYXZlKCJmaWd1cmVzL3RyZW5kX2NvcnJlbGF0aW9uX2hlYXRtYXAucG5nIiwgaGVpZ2h0ID0gMjAsIHdpZHRoID0gMjUsIHVuaXRzID0gImNtIiwgZHBpID0gMzAwKQ0KDQoNCmBgYA0KDQoNCkFsdGhvdWdoIHBsb3RseSBkb2Vzbid0IGNvbnZlcnQgZmFjZXRpbmcgdG9vIGVhc2lseSwgY2FuIEkgdXNlIGdncGxvdGx5IGZvciBob3ZlcmluZyBvdmVyIHZhbHVlcyBmb3IgU2NvdGxhbmQgbWFsZXMgYWxvbmU/DQoNCmBgYHtyfQ0KcCA8LSBjb3JzX2RmICU+JSANCiAgZmlsdGVyKGZyb21fYWdlIDw9IDEwMCwgdG9fYWdlIDw9IDEwMCkgJT4lIA0KICBmaWx0ZXIoc2V4ID09ICJtYWxlIikgJT4lIA0KICBmaWx0ZXIoY29kZSA9PSAiR0JSX1NDTyIpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZnJvbV9hZ2UsIHkgPSB0b19hZ2UsIGZpbGwgPSB2YWx1ZSkpICsgDQogIGdlb21fdGlsZSgpICsgDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKyANCiAgY29vcmRfZXF1YWwoKSANCg0KcCA8LSBjb3JzX2RmICU+JSANCiAgZmlsdGVyKGZyb21fYWdlIDw9IDEwMCwgdG9fYWdlIDw9IDEwMCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBmcm9tX2FnZSwgeSA9IHRvX2FnZSwgZmlsbCA9IHZhbHVlKSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBjb29yZF9lcXVhbCgpICsgDQogIGZhY2V0X2dyaWQoc2V4IH5jb2RlKQ0KDQpwbG90bHk6OmdncGxvdGx5KHApDQoNCmBgYA0KDQpnZ3Bsb3RseSBub3cgd29ya3Mgd2l0aCBmYWNldGVkIHBsb3RzISAoVGhvdWdoIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGxvb2tzIGxpa2UgaXQgY2F1c2VzIHRoaW5ncyB0byBzdHJ1Z2dsZSEpDQoNCkZvciBTY290bGFuZCBhbmQgRW5nbGFuZCAmIFdhbGVzIChpbmRlcGVuZGVudGx5KSwgaG93IGhhdmUgdGhlIGNvcnJlbGF0aW9ucyBjaGFuZ2VkIG92ZXIgdGltZT8gDQoNCkVuZ2xhbmQvV2FsZXMgZmlyc3Q6IA0KDQpgYGB7cn0NCmR0YV90cm5kIDwtIGR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lDQogIGZpbHRlcihjb2RlICVpbiUgYygiR0JSVEVOVyIpKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTAsIDIwMTApKSAgJT4lDQogIGZpbHRlcihhZ2UgPD0gMTA5KSAlPiUgDQogIG11dGF0ZSgNCiAgICBkZWNhZGUgPSBjdXQoeWVhciwgYnJlYWtzID0gc2VxKDE5NTAsIDIwMTAsIGJ5ID0gMTApLCBsYWJlbHMgPSBjKCIxOTUwcyIsICIxOTYwcyIsICIxOTcwcyIsICIxOTgwcyIsICIxOTkwcyIsICIyMDAwcyIpLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpDQogICkgJT4lIA0KICBncm91cF9ieShzZXgsIGRlY2FkZSwgeWVhciwgYWdlKSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUobG9nX21lYW5fTXggPSBsb2cobWVhbl9NeCwgMTApKSAlPiUgIyBDb3JyZWN0aW9uIGZvciBTd2VkZW4NCiAgZ3JvdXBfYnkoc2V4LCBkZWNhZGUpICU+JSANCiAgbmVzdCgpDQoNCg0KY29yc19kZiA8LSBkdGFfdHJuZCAlPiUgDQogIG11dGF0ZShjb3JzID0gbWFwKA0KICAgIGRhdGEsIA0KICAgIGZ1bmN0aW9uKFgpIHsNCiAgICAgIFggJT4lIA0KICAgICAgICBzZWxlY3QoLW1lYW5fTXgpICU+JSANCiAgICAgICAgc3ByZWFkKGFnZSwgbG9nX21lYW5fTXgpICU+JSANCiAgICAgICAgc2VsZWN0KC15ZWFyKSAlPiUgDQogICAgICAgIGNvcigpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgbXV0YXRlKGNvcl9kZiA9IG1hcCgNCiAgICBjb3JzLA0KICAgIGZ1bmN0aW9uKFgpew0KICAgICAgWCAlPiUgDQogICAgICAgIGFzX3RpYmJsZSgpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gcm93bmFtZXMoWCkpICU+JSANCiAgICAgICAgZ2F0aGVyKGtleSA9ICJ0b19hZ2UiLCB2YWx1ZSA9ICJ2YWx1ZSIsIC1mcm9tX2FnZSkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSBhcy5udW1lcmljKGZyb21fYWdlKSwgdG9fYWdlID0gYXMubnVtZXJpYyh0b19hZ2UpKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIHNlbGVjdChzZXgsIGRlY2FkZSwgY29yX2RmKSAlPiUgDQogIHVubmVzdCgpDQoNCg0KY29yc19kZiAlPiUgDQogIGZpbHRlcihmcm9tX2FnZSA8PSAxMDAsIHRvX2FnZSA8PSAxMDApICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZnJvbV9hZ2UsIHkgPSB0b19hZ2UsIGZpbGwgPSB2YWx1ZSkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgY29vcmRfZXF1YWwoKSArIA0KICBmYWNldF9ncmlkKHNleCB+IGRlY2FkZSkNCg0KYGBgDQoNCg0KDQpOb3cgZm9yIFNjb3RsYW5kLiANCg0KYGBge3J9DQpkdGFfdHJuZCA8LSBkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ICE9ICJ0b3RhbCIpICU+JQ0KICBmaWx0ZXIoY29kZSAlaW4lIGMoIkdCUl9TQ08iKSkgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTUwLCAyMDEwKSkgICU+JQ0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGVjYWRlID0gY3V0KHllYXIsIGJyZWFrcyA9IHNlcSgxOTUwLCAyMDEwLCBieSA9IDEwKSwgbGFiZWxzID0gYygiMTk1MHMiLCAiMTk2MHMiLCAiMTk3MHMiLCAiMTk4MHMiLCAiMTk5MHMiLCAiMjAwMHMiKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKQ0KICApICU+JSANCiAgZ3JvdXBfYnkoc2V4LCBkZWNhZGUsIHllYXIsIGFnZSkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGxvZ19tZWFuX014ID0gbG9nKG1lYW5fTXggKyAwLjAwMDAxLCAxMCkpICU+JSAjIENvcnJlY3Rpb24gZm9yIFN3ZWRlbg0KICBncm91cF9ieShzZXgsIGRlY2FkZSkgJT4lIA0KICBuZXN0KCkNCg0KDQpjb3JzX2RmIDwtIGR0YV90cm5kICU+JSANCiAgbXV0YXRlKGNvcnMgPSBtYXAoDQogICAgZGF0YSwgDQogICAgZnVuY3Rpb24oWCkgew0KICAgICAgWCAlPiUgDQogICAgICAgIHNlbGVjdCgtbWVhbl9NeCkgJT4lIA0KICAgICAgICBzcHJlYWQoYWdlLCBsb2dfbWVhbl9NeCkgJT4lIA0KICAgICAgICBzZWxlY3QoLXllYXIpICU+JSANCiAgICAgICAgY29yKCkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBtdXRhdGUoY29yX2RmID0gbWFwKA0KICAgIGNvcnMsDQogICAgZnVuY3Rpb24oWCl7DQogICAgICBYICU+JSANCiAgICAgICAgYXNfdGliYmxlKCkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSByb3duYW1lcyhYKSkgJT4lIA0KICAgICAgICBnYXRoZXIoa2V5ID0gInRvX2FnZSIsIHZhbHVlID0gInZhbHVlIiwgLWZyb21fYWdlKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IGFzLm51bWVyaWMoZnJvbV9hZ2UpLCB0b19hZ2UgPSBhcy5udW1lcmljKHRvX2FnZSkpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgc2VsZWN0KHNleCwgZGVjYWRlLCBjb3JfZGYpICU+JSANCiAgdW5uZXN0KCkNCg0KDQpjb3JzX2RmICU+JSANCiAgZmlsdGVyKGZyb21fYWdlIDw9IDEwMCwgdG9fYWdlIDw9IDEwMCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBmcm9tX2FnZSwgeSA9IHRvX2FnZSwgZmlsbCA9IHZhbHVlKSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBjb29yZF9lcXVhbCgpICsgDQogIGZhY2V0X2dyaWQoc2V4IH4gZGVjYWRlKQ0KDQpgYGANCk5vcnRoZXJuIElyZWxhbmQNCg0KDQpgYGB7cn0NCmR0YV90cm5kIDwtIGR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lDQogIGZpbHRlcihjb2RlICVpbiUgYygiR0JSX05JUiIpKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTAsIDIwMTApKSAgJT4lDQogIGZpbHRlcihhZ2UgPD0gMTA5KSAlPiUgDQogIG11dGF0ZSgNCiAgICBkZWNhZGUgPSBjdXQoeWVhciwgYnJlYWtzID0gc2VxKDE5NTAsIDIwMTAsIGJ5ID0gMTApLCBsYWJlbHMgPSBjKCIxOTUwcyIsICIxOTYwcyIsICIxOTcwcyIsICIxOTgwcyIsICIxOTkwcyIsICIyMDAwcyIpLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpDQogICkgJT4lIA0KICBncm91cF9ieShzZXgsIGRlY2FkZSwgeWVhciwgYWdlKSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUobG9nX21lYW5fTXggPSBsb2cobWVhbl9NeCArIDAuMDAwMDEsIDEwKSkgJT4lICMgQ29ycmVjdGlvbiBmb3IgU3dlZGVuDQogIGdyb3VwX2J5KHNleCwgZGVjYWRlKSAlPiUgDQogIG5lc3QoKQ0KDQoNCmNvcnNfZGYgPC0gZHRhX3RybmQgJT4lIA0KICBtdXRhdGUoY29ycyA9IG1hcCgNCiAgICBkYXRhLCANCiAgICBmdW5jdGlvbihYKSB7DQogICAgICBYICU+JSANCiAgICAgICAgc2VsZWN0KC1tZWFuX014KSAlPiUgDQogICAgICAgIHNwcmVhZChhZ2UsIGxvZ19tZWFuX014KSAlPiUgDQogICAgICAgIHNlbGVjdCgteWVhcikgJT4lIA0KICAgICAgICBjb3IoKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIG11dGF0ZShjb3JfZGYgPSBtYXAoDQogICAgY29ycywNCiAgICBmdW5jdGlvbihYKXsNCiAgICAgIFggJT4lIA0KICAgICAgICBhc190aWJibGUoKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IHJvd25hbWVzKFgpKSAlPiUgDQogICAgICAgIGdhdGhlcihrZXkgPSAidG9fYWdlIiwgdmFsdWUgPSAidmFsdWUiLCAtZnJvbV9hZ2UpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gYXMubnVtZXJpYyhmcm9tX2FnZSksIHRvX2FnZSA9IGFzLm51bWVyaWModG9fYWdlKSkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBzZWxlY3Qoc2V4LCBkZWNhZGUsIGNvcl9kZikgJT4lIA0KICB1bm5lc3QoKQ0KDQoNCmNvcnNfZGYgJT4lIA0KICBmaWx0ZXIoZnJvbV9hZ2UgPD0gMTAwLCB0b19hZ2UgPD0gMTAwKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZyb21fYWdlLCB5ID0gdG9fYWdlLCBmaWxsID0gdmFsdWUpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIGNvb3JkX2VxdWFsKCkgKyANCiAgZmFjZXRfZ3JpZChzZXggfiBkZWNhZGUpDQoNCmBgYA0KDQpgYGB7cn0NCmR0YV90cm5kIDwtIGR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lDQogIGZpbHRlcihjb2RlICVpbiUgYygiR0JSVEVOVyIpKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTUsIDIwMTUpKSAgJT4lDQogIGZpbHRlcihhZ2UgPD0gMTA5KSAlPiUgDQogIG11dGF0ZSgNCiAgICBkZWNhZGUgPSBjdXQoeWVhciwgYnJlYWtzID0gc2VxKDE5NTUsIDIwMTUsIGJ5ID0gMTApLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpDQogICkgJT4lIA0KICBncm91cF9ieShzZXgsIGRlY2FkZSwgeWVhciwgYWdlKSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUobG9nX21lYW5fTXggPSBsb2cobWVhbl9NeCwgMTApKSAlPiUgIyBDb3JyZWN0aW9uIGZvciBTd2VkZW4NCiAgZ3JvdXBfYnkoc2V4LCBkZWNhZGUpICU+JSANCiAgbmVzdCgpDQoNCg0KY29yc19kZiA8LSBkdGFfdHJuZCAlPiUgDQogIG11dGF0ZShjb3JzID0gbWFwKA0KICAgIGRhdGEsIA0KICAgIGZ1bmN0aW9uKFgpIHsNCiAgICAgIFggJT4lIA0KICAgICAgICBzZWxlY3QoLW1lYW5fTXgpICU+JSANCiAgICAgICAgc3ByZWFkKGFnZSwgbG9nX21lYW5fTXgpICU+JSANCiAgICAgICAgc2VsZWN0KC15ZWFyKSAlPiUgDQogICAgICAgIGNvcigpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgbXV0YXRlKGNvcl9kZiA9IG1hcCgNCiAgICBjb3JzLA0KICAgIGZ1bmN0aW9uKFgpew0KICAgICAgWCAlPiUgDQogICAgICAgIGFzX3RpYmJsZSgpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gcm93bmFtZXMoWCkpICU+JSANCiAgICAgICAgZ2F0aGVyKGtleSA9ICJ0b19hZ2UiLCB2YWx1ZSA9ICJ2YWx1ZSIsIC1mcm9tX2FnZSkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSBhcy5udW1lcmljKGZyb21fYWdlKSwgdG9fYWdlID0gYXMubnVtZXJpYyh0b19hZ2UpKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIHNlbGVjdChzZXgsIGRlY2FkZSwgY29yX2RmKSAlPiUgDQogIHVubmVzdCgpDQoNCg0KY29yc19kZiAlPiUgDQogIGZpbHRlcihmcm9tX2FnZSA8PSAxMDAsIHRvX2FnZSA8PSAxMDApICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZnJvbV9hZ2UsIHkgPSB0b19hZ2UsIGZpbGwgPSB2YWx1ZSkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgY29vcmRfZXF1YWwoKSArIA0KICBmYWNldF9ncmlkKHNleCB+IGRlY2FkZSkgKw0KICBsYWJzKA0KICAgIHggPSAiQWdlIiwgeSA9ICJBZ2UiLA0KICAgIHRpdGxlID0gIkNvcnJlbGF0aW9uIGJldHdlZW4gdHJlbmRzIGF0IGluZGl2ZHVhbCBhZ2VzLCBFbmdsYW5kICYgV2FsZXMsIGJ5IGRlY2FkZSINCiAgKQ0KDQpgYGANCg0KDQoNCk5vdyBmb3IgU2NvdGxhbmQuIA0KDQpgYGB7cn0NCmR0YV90cm5kIDwtIGR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lDQogIGZpbHRlcihjb2RlICVpbiUgYygiR0JSX1NDTyIpKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTUsIDIwMTUpKSAgJT4lDQogIGZpbHRlcihhZ2UgPD0gMTA5KSAlPiUgDQogIG11dGF0ZSgNCiAgICBkZWNhZGUgPSBjdXQoeWVhciwgYnJlYWtzID0gc2VxKDE5NTUsIDIwMTUsIGJ5ID0gMTApLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpDQogICkgJT4lIA0KICBncm91cF9ieShzZXgsIGRlY2FkZSwgeWVhciwgYWdlKSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUobG9nX21lYW5fTXggPSBsb2cobWVhbl9NeCArIDAuMDAwMDEsIDEwKSkgJT4lICMgQ29ycmVjdGlvbiBmb3IgU3dlZGVuDQogIGdyb3VwX2J5KHNleCwgZGVjYWRlKSAlPiUgDQogIG5lc3QoKQ0KDQoNCmNvcnNfZGYgPC0gZHRhX3RybmQgJT4lIA0KICBtdXRhdGUoY29ycyA9IG1hcCgNCiAgICBkYXRhLCANCiAgICBmdW5jdGlvbihYKSB7DQogICAgICBYICU+JSANCiAgICAgICAgc2VsZWN0KC1tZWFuX014KSAlPiUgDQogICAgICAgIHNwcmVhZChhZ2UsIGxvZ19tZWFuX014KSAlPiUgDQogICAgICAgIHNlbGVjdCgteWVhcikgJT4lIA0KICAgICAgICBjb3IoKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIG11dGF0ZShjb3JfZGYgPSBtYXAoDQogICAgY29ycywNCiAgICBmdW5jdGlvbihYKXsNCiAgICAgIFggJT4lIA0KICAgICAgICBhc190aWJibGUoKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IHJvd25hbWVzKFgpKSAlPiUgDQogICAgICAgIGdhdGhlcihrZXkgPSAidG9fYWdlIiwgdmFsdWUgPSAidmFsdWUiLCAtZnJvbV9hZ2UpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gYXMubnVtZXJpYyhmcm9tX2FnZSksIHRvX2FnZSA9IGFzLm51bWVyaWModG9fYWdlKSkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBzZWxlY3Qoc2V4LCBkZWNhZGUsIGNvcl9kZikgJT4lIA0KICB1bm5lc3QoKQ0KDQoNCmNvcnNfZGYgJT4lIA0KICBmaWx0ZXIoZnJvbV9hZ2UgPD0gMTAwLCB0b19hZ2UgPD0gMTAwKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZyb21fYWdlLCB5ID0gdG9fYWdlLCBmaWxsID0gdmFsdWUpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIGNvb3JkX2VxdWFsKCkgKyANCiAgZmFjZXRfZ3JpZChzZXggfiBkZWNhZGUpDQoNCmBgYA==